繼承是物件導向程式設計的三個主要特性之一,另外兩個是封裝和多型。
繼承可讓您建立新類別以重複使用、擴充和修改其他類別中定義的行為。
成員被繼承的類別稱為「基底類別」,而繼承這種成員的類別即稱為「衍生類別」。
衍生類別只能有一個基底類別。 不過,繼承是可轉移的。如果 ClassC 衍生自 ClassB,而 ClassB 衍生自 ClassA,則 ClassC 會繼承在 ClassB 和 ClassA 中宣告的所有成員。
封裝(Encapsulation):簡單說就是我們前幾天提到的類別,透過存取層級關鍵字來決定哪些屬性與方法是公開或私有的。
多型(Polymorphism):個人認為與多載的差異為,多載為單一類別存在同名稱但不同參數的方法,而多型則是不同類別共同存在的同名方法。
衍生類別可以重複使用基底類別中的程式碼,而不需要重新實作。
您可以在衍生類別中新增更多成員。透過這種方式,衍生類別等於是擴充了基底類別的功能。
也就是說當今天使用了繼承,ClassB 繼承了 ClassA 時不需要針對 ClassA 原有的方法重新撰寫內容。在這邊示範一下
class Program {
static void Main (string[] args) {
ClassB classB = new ClassB ();
classB.SetA (6);
classB.PrintA ();
Console.ReadKey ();
}
}
class ClassA {
protected int a = 0;
public void PrintA () {
Console.WriteLine (a);
}
}
class ClassB : ClassA {
public void SetA (int number) {
a = number;
}
}
可以的話還是請讀者親自改改看,為什麼 ClassB 在 new 生成物件時,要由 SetA 來更改 a 值,繼承時什麼可以使用什麼不能使用,以及生成物件後哪些東西可以被存取,這都在第十天的存取層級關鍵字有說明了。
所以透過上述範例可以了解繼承的用法,再舉一個示範用途的常見例子,比如說動物,每種動物都有屬於自己的一些特性,如鳥會飛、魚會游泳等等,但他們都有共同生為動物的吃與睡覺。
所以基底類別為動物,衍生類別就是鳥與魚。
class Program {
static void Main (string[] args) {
Bird bird = new Bird ();
bird.Eat ();
bird.Sleep ();
bird.Fly ();
Fish fish = new Fish ();
fish.Eat ();
fish.Sleep ();
fish.Swim ();
Console.ReadKey ();
}
}
class Animal {
public void Eat () {
Console.WriteLine ("進食");
}
public void Sleep () {
Console.WriteLine ("閉眼睡覺");
}
}
class Bird : Animal {
public void Fly () {
Console.WriteLine ("飛行");
}
}
class Fish : Animal {
public void Swim () {
Console.WriteLine ("游泳");
}
}
當然每種動物的吃與睡覺的方法不一樣,所以需要去重寫內容,例如睡覺方法,魚不會閉眼啊,所以我們需要透過 override 或 new 關鍵字重新定義方法。
override:需要配合 virtual 或 abstract 使用,基底內別需要將欲 override 的方法加上這兩個關鍵字,才可以進行衍生類別的方法覆寫。
virtual(虛擬方法) 與 abstract(抽象方法) 簡單來說就是 virtual 能實作方法,而 abstract 不能實作方法的差別。
new:將衍生類別重新定義一個同名稱的方法,但已存在的基底類別方法依然存在,所以在操作基底類別時,若使用基底類別的方法去呼叫這個同名稱的方法,則會出現基底類別的方法。
override 與 new 簡單來說就是 override 是覆寫基底類別的方法,而 new 是衍生類別重新撰寫一個方法。
這邊示範 override 來改寫睡覺方法
class Program {
static void Main (string[] args) {
Bird bird = new Bird ();
bird.Eat ();
bird.Sleep ();
bird.Fly ();
Fish fish = new Fish ();
fish.Eat ();
fish.Sleep ();
fish.Swim ();
Console.ReadKey ();
}
}
class Animal {
public void Eat () {
Console.WriteLine ("進食");
}
public virtual void Sleep () {
Console.WriteLine ("閉眼睡覺");
}
}
class Bird : Animal {
public void Fly () {
Console.WriteLine ("飛行");
}
}
class Fish : Animal {
public void Swim () {
Console.WriteLine ("游泳");
}
public override void Sleep () {
Console.WriteLine ("開眼睡覺");
}
}
若衍生類別與基底類別擁有相同名稱的成員,但想要存取基底類別的成員時,則可以使用 base 關鍵字來表示基底類別。
若你想要避免被衍生,可以將類別本身或其成員宣告為密封 sealed,即可防止其他類別繼承該類別,或繼承其任何成員。
了解類別的這三個特性,就能撰寫一個好的物件導向程式,由基底類別為物件的共有成員,在為不同處生成其衍生類別,好處當然就是共有程式碼時,易讀易改。壞處則是新手不懂時,會不知道怎麼去拆物件,讀 code 時若不清楚,則好像要東看一塊西看一塊,所以物件導向該有什麼規範也要去了解,幫助每一個工程師去讀 code 寫 code。
題外話:個人比較少用繼承,而是會比較常用明天所介紹的「介面」,概念與繼承有極高相似。然後在引用官方說明時,有提到衍生類別只能有一個基底類別,在之前看的文章裡,可能以後 C# 會新增一個特性來支援多重繼承,但我忘記關鍵字了,所以可以期待一下。
參考連結
繼承 (C# 程式設計手冊) | Microsoft Docs
sealed (C# 參考) | Microsoft Docs